package com.javacoo.xservice.base.security.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.javacoo.xservice.base.security.coder.SHACoder;
import com.javacoo.xservice.base.utils.FastJsonUtil;

/**
 * 签名工具类
 * <p>说明:</p>
 * <li></li>
 * @author DuanYong
 * @since 2017年7月5日上午9:35:36
 */
public class SignUtil {
    /**
     * 默认编码
     */
    private static final String CHARSET = "UTF-8";
	/**
	 * 签名
	 * <p>说明:</p>
	 * <li>1:将需要签名的参数按参数名升序排序</li>
	 * <li>2:请求参数串=paramName1paramValue1paramName2paramValue2...</li>
     * <li>3:将应用密钥分别添加到 请求参数串+随机数+时间戳 的头部和尾部:secret+请求参数字符串+随机数+时间戳+secret</li>
     * <li>4:对该字符串进行 SHA256 运算，得到一个byte数组</li>
     * <li>5:将该byte数组转换为十六进制的字符串，该字符串即是这些请求参数对应的签名</li>
     * <li>6:HEX(SHA256(secret+请求参数字符串+随机数+时间戳+secret))</li>
	 * @author DuanYong
	 * @param params 需要签名的参数
	 * @param secretKey 应用密钥
	 * @return 签名值
	 * @since 2017年7月5日上午9:35:23
	 */
	public final static String sign(Map<String, String> params,String nonce, String timestamp, byte[] secretKey) {
		if (params == null || params.size() == 0 || secretKey == null){
			return null;
		}
		try {
			List<String> paramNames = new ArrayList<>(params.size());
			paramNames.addAll(params.keySet());
			Collections.sort(paramNames);

			StringBuilder sb = new StringBuilder();
			for (String paramName : paramNames) {
				Object v = params.get(paramName);
				if(v instanceof JSONArray || v instanceof JSONObject){
					continue;
				}
				sb.append(paramName).append(v);
			}
            sb.append(nonce);
            sb.append(timestamp);
            List<byte[]> bytes = new ArrayList<>();
            bytes.add(secretKey);
            bytes.add(sb.toString().getBytes(CHARSET));
            bytes.add(secretKey);
            byte[] data = addBytes(bytes);
            return byte2hex(SHACoder.encodeSHA256(data));
		} catch (Exception e) {
			return null;
		}
	}
    /**
     * 简单签名
	 * <p>说明:</p>
     * <li>1:请求参数串=请求报文体中 parameter 对象转换为 json字符串(统一用fastjson)</li>
	 * <li>2:将应用密钥分别添加到 请求参数串+随机数+时间戳 的头部和尾部:secret+请求参数字符串+随机数+时间戳+secret</li>
	 * <li>3:对该字符串进行 SHA256 运算，得到一个byte数组</li>
	 * <li>4:将该byte数组转换为十六进制的字符串，该字符串即是这些请求参数对应的签名</li>
     * <li>5:HEX(SHA256(secret+请求参数字符串+随机数+时间戳+secret))</li>
	 * @author DuanYong
	 * @param param 需要签名的参数
	 * @param nonce 随机数
	 * @param timestamp 时间戳
	 * @param secretKey 应用密钥
	 * @return 签名值
     * @since 2017年11月2日上午11:16:50
     */
	public final static String sign(String param,String nonce, String timestamp,  byte[] secretKey) {
		if (param == null || param.length() == 0){
			return null;
		}
		try {
			StringBuilder sb = new StringBuilder();
			sb.append(param);
			sb.append(nonce);
			sb.append(timestamp);
			List<byte[]> bytes = new ArrayList<>();
			bytes.add(secretKey);
			bytes.add(sb.toString().getBytes(CHARSET));
			bytes.add(secretKey);
			byte[] data = addBytes(bytes);
			return byte2hex(SHACoder.encodeSHA256(data));
		} catch (Exception e) {
			return null;
		}
	}
	/**
     * 简单签名
	 * <p>说明:</p>
	 * <li>1:将应用密钥分别添加到 请求参数串 的头部和尾部：secret+请求参数字符串+secret</li>
	 * <li>2:对该字符串进行 SHA256 运算，得到一个二进制数组</li>
	 * <li>3:将该二进制数组转换为十六进制的字符串，该字符串即是这些请求参数对应的签名</li>
	 * @author DuanYong
	 * @param param 需要签名的参数
	 * @param secretKey 应用密钥
	 * @return 签名值
     * @since 2017年11月2日上午11:16:50
     */
	public final static String sign(String param, byte[] secretKey) {
		if (param == null ){
			return null;
		}
		try {
			StringBuilder sb = new StringBuilder();
			//sb.append(secretKey);
			sb.append(param);
			//sb.append(secretKey);
			List<byte[]> bytes = new ArrayList<>();
			bytes.add(secretKey);
			bytes.add(sb.toString().getBytes(CHARSET));
			bytes.add(secretKey);
			byte[] data = addBytes(bytes);
			return byte2hex(SHACoder.encodeSHA256(data));
		} catch (Exception e) {
			return null;
		}
	}
    /**
     * 将该二进制数组转换为十六进制的字符串
     * <p>说明:</p>
     * <li></li>
     * @author DuanYong
     * @param bytes
     * @return
     * @since 2017年7月5日上午9:34:37
     */
	private static String byte2hex(byte[] bytes) {
		StringBuilder sign = new StringBuilder();
		for (int i = 0; i < bytes.length; i++) {
			String hex = Integer.toHexString(bytes[i] & 0xFF);
			if (hex.length() == 1) {
				sign.append("0");
			}
			sign.append(hex);
		}
		return sign.toString();
	}
	
	/**
	 * 在json字符串中查找指定key对应的值
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @param jsonStr
	 *            json字符串
	 * @param key
	 *            指定key
	 * @return key对应的值/空
	 * @since 2017年7月5日上午10:48:30
	 */
	public static String findString(String jsonStr, String key) {
		String keyRegex = "\"" + key + "\":\"(.*?)\"";
		return findStringBy(jsonStr,keyRegex);
	}
	/**
	 * 在json字符串中查找指定key,与endKey对应的JSON值
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param jsonStr json字符串
	 * @param key 关键字
	 * @param endKey 结束关键字
	 * @return JSON值
	 * @since 2017年10月19日下午2:40:15
	 */
	public static String findJsonString(String jsonStr, String key,String endKey) {
		String keyRegex = "\"" + key + "\":(.*?)"+endKey+"+$";
		return findStringBy(jsonStr,keyRegex);
	}
	/**
	 * 在JSON字符串中查找指定正则表达式对应的值
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param jsonStr json字符串
	 * @param regex 正则表达式
	 * @return 正则表达式对应的值
	 * @since 2017年10月19日下午2:52:55
	 */
	private static String findStringBy(String jsonStr,String regex){
		try {
			Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
			Matcher matcher = pattern.matcher(jsonStr);
			while (matcher.find()) {
				return matcher.group(1);
			}
		} catch (Exception e) {
		}
		return "";
	}
	/**
	 * 云端验证签名
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param signedMsg 上送的签名字符串
	 * @param context 请求数据
	 * @param timestamp 时间戳
	 * @param nonce 随机数
	 * @param secretkey 安全KEY
	 * @since 2017年11月28日下午5:55:25
	 */
	public static boolean cloudVerifySign(String signedMsg,String context,String nonce,String timestamp,String secretkey){
	    try{
	    	 byte[] sei = getSecretKey(secretkey);

	    	 String sign = sign(FastJsonUtil.stringToCollect(context),nonce,timestamp,sei);
	    	 if(null == sign || null == signedMsg || !sign.equals(signedMsg)){
	    		 return false;
	    	 }
	 	   return true;
	    }catch(Exception e){
	    	e.printStackTrace();
	    }
	    return false;
	}
	/**
	 * 客户端验签
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param signedMsg 上送的签名字符串
	 * @param context 数据
	 * @param secretkey 安全KEY
	 * @return
	 * @since 2017年11月28日下午6:12:12
	 */
	public static boolean clientVerifySign(String signedMsg,String context,String secretkey){
	    try{
	    	byte[] sei = getSecretKey(secretkey);
	    	 String sign = sign(context,sei);
	    	 if(null == sign || null == signedMsg || !sign.equals(signedMsg)){
	    		 return false;
	    	 }
	 	   return true;
	    }catch(Exception e){
	    	e.printStackTrace();
	    }
	    return false;
	}
	/**
	 * 云端签名
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param param 参数
	 * @param secretKey 安全密钥
	 * @return 签名值
	 * @since 2017年11月28日下午6:24:09
	 */
	public final static String cloudSign(String param, String secretKey) {
		if (param == null 
				|| secretKey == null || secretKey.length() == 0){
			return null;
		}
		try {
			byte[] sei = getSecretKey(secretKey);
			return sign(param,sei);
		} catch (Exception e) {
			return null;
		}
	}
	/**
	 * 客户端签名
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param param 参数
	 * @param nonce 随机数
	 * @param timestamp 时间戳
	 * @param secretKey 安全KEY
	 * @return 签名值
	 * @since 2017年11月28日下午7:05:28
	 */
	public final static String clientSign(String param,String nonce, String timestamp,  String secretKey) {
		if (param == null || param.length() == 0
				|| secretKey == null || secretKey.length() == 0){
			return null;
		}
		try {
			byte[] sei = getSecretKey(secretKey);
			return sign(FastJsonUtil.stringToCollect(param),nonce,timestamp,sei);
		} catch (Exception e) {
			return null;
		}
	}
	 protected static byte[] getSecretKey(String secretkey) throws Exception{
	   	  return secretkey.getBytes(CHARSET);
     }
	 /**
	  * 合并byte
	  * <p>说明:</p>
	  * <li></li>
	  * @author DuanYong
	  * @param bytes
	  * @return
	  * @since 2017年11月29日上午8:54:51
	  */
	 private static byte[] addBytes(List<byte[]> bytes){
		 int len = 0;
		 for(byte[] b : bytes){
			 len = len + b.length;
		 }
		 byte[] newData = new byte[len];
		 int tempLen = 0;
		 for(byte[] b : bytes){
			 System.arraycopy(b,0,newData,tempLen,b.length);
			 tempLen = tempLen+b.length;
		 }
		 return newData;
	 }
	public static void main(String[] args) throws Exception {
		System.out.println(byte2hex(SHACoder.encodeSHA1("abcdefappKey000001timestamp1391184000195abcdef".getBytes("UTF-8"))));
	}
}
